package com.chenbei.schedule.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chenbei.common.exception.LogicHandleException;
import com.chenbei.common.util.ValidatorUtil;
import com.chenbei.schedule.domain.ScheduleJob;
import com.chenbei.schedule.enums.JobConcurrentEnums;
import com.chenbei.schedule.enums.JobStatusEnum;
import com.chenbei.schedule.job.AbstractScheduleJob;
import com.chenbei.schedule.job.DisallowConcurrentScheduleJobExecution;
import com.chenbei.schedule.job.ScheduleJobExecution;
import com.chenbei.schedule.mapper.ScheduleJobMapper;
import com.chenbei.schedule.service.ScheduleJobService;
import com.chenbei.schedule.vo.req.ScheduleJobReq;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.Objects;

@Service
@Slf4j
public class ScheduleJobServiceImpl extends ServiceImpl<ScheduleJobMapper, ScheduleJob> implements ScheduleJobService {

    @Autowired
    private Scheduler scheduler;

    @Override
    public void add(ScheduleJob scheduleJob) {
        if (!ValidatorUtil.isValidQuartzCron(scheduleJob.getCronExpress())) {
            throw new LogicHandleException("Cron表达式格式不正确");
        }

        try {
            // 持久化
            save(scheduleJob);

            JobKey jobKey = new JobKey(scheduleJob.getName(), scheduleJob.getGroupName());
            TriggerKey triggerKey = new TriggerKey(scheduleJob.getName(), scheduleJob.getGroupName());
            if (scheduler.checkExists(jobKey) || scheduler.checkExists(triggerKey)) {
                throw new LogicHandleException("任务或触发器已存在，不能重复添加");
            }

            registerScheduleJob(scheduleJob);
        } catch (SchedulerException e) {
            throw new LogicHandleException("任务添加到调度器失败", e);
        }
    }

    @Override
    public void update(ScheduleJob scheduleJob) {
        if (!ValidatorUtil.isValidQuartzCron(scheduleJob.getCronExpress())) {
            throw new LogicHandleException("Cron表达式格式不正确");
        }
        try {
            JobKey jobKey = new JobKey(scheduleJob.getName(), scheduleJob.getGroupName());
            if (scheduler.checkExists(jobKey)) {
                scheduler.deleteJob(jobKey);        // 删除任务的同时，与之绑定的触发器也一并会被删除。
            }
            registerScheduleJob(scheduleJob);        // 注册调度任务
            updateById(scheduleJob);
        } catch (SchedulerException e) {
            log.error("[任务调度]-[更新异常]-[异常信息:{}]", e.getMessage());
            throw new LogicHandleException("任务添加到调度器失败", e);
        }
    }

    @Override
    public void runOnceJob(Long id) {
        ScheduleJob scheduleJob = getById(id);
        if (scheduler != null) {
            JobKey jobKey = new JobKey(scheduleJob.getName(), scheduleJob.getGroupName());
            try {
                if (scheduler.checkExists(jobKey)) {
                    scheduler.triggerJob(jobKey);
                }
            } catch (SchedulerException e) {
                log.error("[任务调度]-[启动一次异常]-[异常信息:{}]", e.getMessage());
            }
        }
    }

    @Override
    public void start(Long id) {
        ScheduleJob scheduleJob = getById(id);
        if (scheduleJob != null) {
            JobKey jobKey = new JobKey(scheduleJob.getName(), scheduleJob.getGroupName());
            try {
                if (scheduler.checkExists(jobKey)) {
                    scheduler.resumeJob(jobKey);

                    scheduleJob.setStatus(JobStatusEnum.RUNNING.getStatus());
                    updateById(scheduleJob);
                }
            } catch (SchedulerException e) {
                log.error("[任务调度]-[启动异常]-[异常信息:{}]", e.getMessage());
            }
        }
    }

    @Override
    public void pause(Long id) {
        ScheduleJob scheduleJob = getById(id);
        if (scheduleJob != null) {
            JobKey jobKey = new JobKey(scheduleJob.getName(), scheduleJob.getGroupName());
            try {
                if (scheduler.checkExists(jobKey)) {
                    scheduler.pauseJob(jobKey);     // 停止任务，与之绑定的所有调度器都会停止。
                    scheduleJob.setStatus(JobStatusEnum.PAUSED.getStatus());
                    updateById(scheduleJob);
                }
            } catch (SchedulerException e) {
                log.error("[任务调度]-[暂停异常]-[异常信息:{}]", e.getMessage());
            }
        }
    }

    @Override
    public void delete(Long id) {
        ScheduleJob scheduleJob = getById(id);
        if (scheduleJob != null) {
            removeById(id);
            JobKey jobKey = new JobKey(scheduleJob.getName(), scheduleJob.getGroupName());
            try {
                if (scheduler.checkExists(jobKey)) {
                    scheduler.deleteJob(jobKey);
                }
            } catch (SchedulerException e) {
                log.error("[任务调度]-[删除任务]-[异常信息:{}]", e.getMessage());
            }
        }
    }

    @Override
    public Page<ScheduleJob> pageList(ScheduleJobReq req) {
        LambdaQueryWrapper<ScheduleJob> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.orderByDesc(ScheduleJob::getId);
        if (StrUtil.isNotBlank(req.getName())) {
            lambdaQueryWrapper.like(ScheduleJob::getName, req.getName());
        }
        if (StrUtil.isNotBlank(req.getGroupName())) {
            lambdaQueryWrapper.like(ScheduleJob::getGroupName, req.getGroupName());
        }
        if (req.getStatus() != null && req.getStatus() >= 0) {
            lambdaQueryWrapper.eq(ScheduleJob::getStatus, req.getStatus());
        }
        if (req.getLatestExecuteStatus() != null && req.getLatestExecuteStatus() >= 0) {
            lambdaQueryWrapper.eq(ScheduleJob::getLatestExecuteStatus, req.getLatestExecuteStatus());
        }

        Page<ScheduleJob> pageConfig = new Page<>(req.getPageNum(), req.getPageSize());
        return page(pageConfig, lambdaQueryWrapper);
    }

    /**
     * 注册调度任务
     * @param scheduleJob 调度任务实例
     */
    private void registerScheduleJob(ScheduleJob scheduleJob) throws SchedulerException {
        JobKey jobKey = new JobKey(scheduleJob.getName(), scheduleJob.getGroupName());
        TriggerKey triggerKey = new TriggerKey(scheduleJob.getName(), scheduleJob.getGroupName());
        Class<? extends AbstractScheduleJob> clazz = Objects.equals(scheduleJob.getIsConcurrent(), JobConcurrentEnums.DISALLOW.getConcurrent()) ? DisallowConcurrentScheduleJobExecution.class : ScheduleJobExecution.class;
        // 构建JobDetail
        JobDetail jobDetail = JobBuilder.newJob(clazz)
                .withIdentity(jobKey)
                .usingJobData("jobId", scheduleJob.getId())
                .storeDurably()
                .build();

        // 构建Trigger
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpress());
        CronTrigger cronTrigger = TriggerBuilder.newTrigger()
                .withIdentity(triggerKey)
                .withSchedule(cronScheduleBuilder)
                .startAt(new Date(Integer.MAX_VALUE))
                .build();
        // 添加任务
        scheduler.scheduleJob(jobDetail, cronTrigger);

        // 立即暂停任务
        scheduler.pauseJob(jobKey);
    }

    /**
     * 根据类名获取 Class 对象
     * @param className 全类名
     * @return Class
     */
    @SuppressWarnings("unchecked")
    private Class<? extends QuartzJobBean> getClass(String className) {
        try {
            Class<?> aClass = Class.forName(className);
            if (!QuartzJobBean.class.isAssignableFrom(aClass)) {
                throw new LogicHandleException("指定的类未继承 QuartzJobBean：" + className);
            }
            return (Class<? extends QuartzJobBean>) aClass;
        } catch (ClassNotFoundException e) {
            log.error("无法找到对应的 Job 类：{}", className, e);
            throw new LogicHandleException("无法找到对应的 Job 类：" + e.getMessage());
        }
    }
}
